Using wimgapi.dll with VB6 - WimApplyImage causes VB6 IDE to freeze?
I'm writing some code to use the wim imaging api wimgapi.dll with VB6.
The code works 100% on capture, with the callback function correctly getting progress information and updating my form with progress bar etc. This works fine when run from within the IDE or from the compiled .exe.
The apply image code is a problem though - the WimApplyImage code locks up the IDE part way through. When run from the compiled exe, I get no progress messages to the callback - I do get all the process messages though, and it actually works in that it correctly applies the image, but it stops updating my form until it exits the WIMApplyImage function.
Anyone have any ideas?
I've tried registering the callback both immediately before the WimApplyImage call (passing hWim handle to it) as well as right up front (passing only the address of the callback) - same result.
Uses the same callback function as the capture - capture works 100%.
To me it is almost like the standard windows messages stop at some point in the WIMApplyImage function.
Similarly the imagex.exe also exhibits this behavior...i tried to code around the wimgapi, with no luck. Is it possible to share some code ? Thanx in advance for any help.
OK i have run into the same problem. I have asked around and expect an
answer soon. The only difference i is i am coding in vb 2005, and
using the .net framework 2.0 in winpe. But again the results are
exactly the same as yours. In the meantime i am suspecting that
the flags used in the CreateAPI are incorrect, for instance using 4 to
open if exist or create, as oppose to create new always. I've
gotten mixed results. Also the other problem i've seen is the
fact you cannot apply an image, until one has been captured.
Cheers
After discussion, it is found that the problem lies with the dll
spawning a new instance of the form.. a bug which lies in the
dll. To prove that this is the issue, do a form1.show, and you
should see the new form with the updated progress.
Deab, could you give me an example of how you're getting your form's progress bar updated? I've gotten so far as to get the wParam for % done (if found it to be message ID 38008), and my CallBackHandler function can do a msgbox with the info, but for the life of me my callbackhandler function can't update the progress bar, or anything else on the form for that matter.
Thanks!
I have gotten it to work...instead of using the default form, create a default instance in a module. Then create a fcallback unction for that instance.
Wow, thanks wreckless for replying so quick -- do you have any code to demonstrate what you mean?
"default form".. I just have a main form I've created as the UI, and all of my code is in this form.
Thanks again.
ok in a module:Public f As New frmmain
Public Sub main()
Application.Run(f)
End Sub
Then in your form:
Public Delegate Function UpdateProgressDelegate(ByVal dwMessageId As Integer, ByVal wparam As IntPtr, ByVal lparam As IntPtr, ByVal lpvUserData As IntPtr) As Integer
Public m_progDelegate As UpdateProgressDelegate
Dim m_bCapture As Boolean = False
Public Function UpdateProgress(ByVal dwMessageId As Integer, ByVal wparam As IntPtr, ByVal lparam As IntPtr, ByVal lpvUserData As IntPtr) As Integer
Select Case dwMessageId
Case Is = clswim.WIM_MSG_PROGRESS
If m_bCapture Then
ProgressBar2.Value = (CInt(wparam))
Label1.Text = CInt(lparam) & " remaining"
Else
ProgressBar3.Value = (CInt(wparam))
Label3.Text = CInt(lparam) & " remaining"
End If
Application.DoEvents()
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_FILEINFO
'If Not Form1.GroupBox5.Enabled Then
' Form1.Label4.Text = Marshal.PtrToStringAuto(wParam)
' Marshal.PtrToStructure(lParam, W32FINDDATA)
'Else
' Form1.Label4.Text = Marshal.PtrToStringAuto(wParam)
' Marshal.PtrToStructure(lParam, W32FINDDATA)
'End If
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_PROCESS
If Not GroupBox5.Enabled Then
Label2.Text = Marshal.PtrToStringAuto(wparam)
Else
Label4.Text = Marshal.PtrToStringAuto(wparam)
End If
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_STEPIT
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_SETRANGE
If Not GroupBox5.Enabled Then
Label10.Text = CInt(lparam) & " files to capture"
Else
Label9.Text = CInt(lparam) & " files to apply"
End If
Application.DoEvents()
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_SETPOS
If Not GroupBox5.Enabled Then
wim2.no_of_files = CInt(lparam) & " files captured"
Else
wim2.no_of_files = CInt(lparam) & " files applied"
End If
Application.DoEvents()
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_SCANNING
If Not GroupBox5.Enabled Then
Label11.Text = CInt(wparam) & " folders scanned"
Label12.Text = CInt(lparam) & " files scanned"
End If
Application.DoEvents()
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_ERROR
wim2.int_err_code = Marshal.PtrToStringAuto(lparam) & " error"
wim2.int_err_code_file = Marshal.PtrToStringAuto(wparam) & " caused an error"
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_COMPRESS
'compressed_file = Marshal.PtrToStringAuto(wParam) & " - compressed file"
'Marshal.PtrToStringAuto(lParam)=0 ' Used to manually stop compression
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_ALIGNMENT
'file_fail2coa = Marshal.PtrToStringAuto(wParam) & " - failed to apply or capture"
'Marshal.PtrToStringAuto(lParam)=0 ' Used to align boundary
Return clswim.WIM_MSG_SUCCESS
Case Is = clswim.WIM_MSG_SPLIT
Return clswim.WIM_MSG_SUCCESS
Application.DoEvents()
End Select
End Function
That is spectacular. Thanks so much wreckless.
Side conversation - what are you developing for? We're probably all reinventing the wheel creating a GUI for imagex. Meanwhile Microsoft will come out with one eventually too... 
This solution won't work for VB6 though... any suggestions? Do we wait for MS to fix this apparent bug?
I do have a "kludge" workaround for VB6.....
1. ensure the Apply operation is fired from a timer (this keeps the form responsive)
2. from the callback update a registry key
3. from the form, query the said registry key as often as required and update the progress bar from this.
I'm not enough of a Windows coder to explain with certainty why this works... presumably something to do with breaking the path of execution back to the form.... but it does work. The overhead doesn't appear to be too punishing either.
Cheers.
why use vb6, .net can be used in WINPE...i have developed my own suite of tools in WINPE...my reason for doing it with the api is to not use double the overhead in WINPE, ie using my app which will kick off imagex using the create pipe api and reading the stdout pipe...i can call the api directly. my dot net solution works like a champ.
Say what? Since when has .NET worked under WinPE? Even the developers I spoke to at the Seattle MMS indicated it could be a couple of years before .NET is in WinPE.....
I'm intrigued :-) What's the secret?
Interesting - thanks guys.
I'm working with a Microsoft contact to try and get this issue resolved - seems that we are all having to come up with workarounds - doesn't make sense especially given the product is still pre-release. To me it would make the most sense to fix the .dll now before the RTM build (which will of course be imaged using the .dll).
wrecklesswun - in 100 words or less, where do you believe the problem lies? The .dll? The WIMApplyImage function specifically? I will pass your findings to the MS guys as well. For me the capture function is fine, just the apply function has an issue.
Very interesting that you had the same problem in VB 2005. I would have coded my app in VB 2005, but wanted to keep my PE image size down - what sort of overhead is involved in putting the .NET 2.0 Framework in WinPE? must add a fair chunk to the image size.
Are you using the Vista PE (2.0) or the 2005 version?
Direct from Microsoft:
ProgressBar Update:
The reason the ProgressBar
is not updated is due to the behaviour of MyForms. (By calling Form1.ProgressBar3.Value, we are using the
Default instance of the form, which in turn is instantiated by
MyForms)
From the “Threading” section in this article (
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/vbmy.asp):
“Instances of classes available from My are exposed in a way that alleviates threading concerns because instances of
My objects are provided per-thread. That is, the instance of My.Computer returned on thread 1 will be different than an
instance of My.Computer returned on thread 2
.
“
So this behaviour is true for MyForms too, because of the callback
thread.
To resolve ths,
we need create a delegate of the CallBack in Form1 and use
a global instance of the form
To do so, Add a module of the to the project and create a global instance of form1,
create a delegate
of MessageCallback and a same function (say,
“MessageCallback_Form1”) in Form1 and in the
clsWim.MessageCallBack, invoke the
“MessageCallback_Form1”
function from MessageCallback and update the progressbar in
MessageCallback_Form1.
Just as a history, what i did is instead of creating an application, i created a class to simplify using the dll...so then i add my class and instantiate the class ie
Dim whatever as new clswimgapi...
so now to apply it's as simple as
with whatever
.applpypath="" etc
end with
And oh yeah...as a test i added the entire .net framework to winpe...about 250 mb was added to the image size. As i am not a seasoned coder, i hadn't investigated whether or not it is possible to slim this down, like maybe using the compact framework? I assumed that just using the assemblies you need with your app would work, but again i haven't tried. maybe this is a question i can pose to MS...