News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Control Panel Applet

Started by fearless, December 04, 2007, 03:13:11 PM

Previous topic - Next topic

fearless

Hi, I've been trying to put together a small control panel applet and seem to have run into a problem.

Ive been reading up in the Win32 Help documentation regarding control panel applets and how they are similar to dll files but only export a CPlApplet main function.

Ive searched a few forums for examples and found a few code snippets to help point me in the right direction.

I get a warning when compiling (from within RadASM)

M:\Masm32\Bin\ML.EXE /c /coff /Cp /nologo /I"M:\Masm32\Include" "CtrlPanel.asm"
Assembling: CtrlPanel.asm
M:\Masm32\Bin\LINK.EXE /SUBSYSTEM:WINDOWS /RELEASE /DLL /DEF:CtrlPanel.def /LIBPATH:"M:\Masm32\Lib" /OUT:"CtrlPanel.dll" "CtrlPanel.obj"
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

   Creating library CtrlPanel.lib and object CtrlPanel.exp
CtrlPanel.dll : warning LNK4086: entrypoint "_CPlApplet@16" is not __stdcall with 12 bytes of arguments; image may not run


I tried setting the main CPlApplet PROTO to include STDCALL but i still get this warning.
I copy the CtrlPanel.dll (renamed to .cpl) into c:\windows and register it with windows by creating a string in HKCU\Control Panel\MMCPL to point to this ("c:\windows\CtrlPanel.cpl") as suggested in the Win32 help. However on trying to load control panel it crashes silently. I don't see any crash dialog, and only once i see DrWatson in task manager, but i cannot load explorer.exe anymore. I have to kill explorer.exe off and then start new task from Task Manager to restore.

Am i compiling this wrong? have a missed something regarding the PROTO for the main CPlApplet? or is it something else that is causing the problem?

Attached is the project code i am using. I haven't got round to showing any dialog boxes yet as i cant even get it to show up in control panel. Additionally if someone has a working example they can point me to so i can compare and see what i have missed, that would be great.

Thanks in advance.

Best regards

KSR

http://homepage.eircom.net/~keithsrobertson/CtrlPanel.zip
ƒearless

donkey

From the error you have shown and without any code to judge by, the CPLApplet function requires 4 arguments and you are only passing 3 (12 bytes). It is difficult to say that your PROTO is wrong if you don't post the proto  :eek

By the way I never install or download software from the forum except from people I know, post the appropriate sections of code and you will get more help.

LONG CPlApplet(     
    HWND hwndCPl,
    UINT uMsg,
    LPARAM lParam1,
    LPARAM lParam2
);


You know this would be a good subject for another one of my sample projects, perhaps this weekend.

Donkey
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

sinsi

Shouldn't the start address be DLLMain and not CplApplet?
Quote
BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,
  DWORD fdwReason,
  LPVOID lpvReserved
);
Light travels faster than sound, that's why some people seem bright until you hear them.

fearless

Thanks for the replies. Yes, sinsi, i went back and tried it with DllEntry (DllMain) at the start and it works. The control panel applet doesnt crash or hang explorer. The proto for this was the same as before, and when compiling this time, it didnt complain - just by adding a DllMain and setting End DllMain to this it compiled fine.

When i go into control panel i can see the item, yet it doesnt have any icon (a standard default icon is used) and no text or description is visible. When i double click it, it does process the CPL_INIT as i see the messagebox popup that i put in to test, and the CPL_DBLCLICK also fires - with another messagebox confirming this. Ive tried playing around with using CPL_DYNAMIC_RES for idIcon, idName and idInfo members of CPLINFO (as suggested in a snippet i found, this is supposed to send a CPL_NEWINQUIRE message - but no joy there). So ive made a little progress but still no visible icon or name and description let alone any dialogboxes.

Here is the code im working with so far, bit messy in places as i commented out a few things i tried, and placed msgboxes to test various things, ill keep at it, and see if i get anywhere - but if anyone has any pointers or ideas why it doesnt show the icon and text in control panel then that would be helpful :D

CPlApplet PROTO :DWORD,:DWORD,:DWORD,:DWORD

DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
; invoke MessageBox,NULL,addr LoadMsg,addr AppName,MB_OK
Invoke GetModuleHandle, CTEXT("CtrlPanel.cpl")
mov hCpl, eax
mov eax, hInst
mov hInstance, eax
.endif
mov  eax,TRUE
ret
DllEntry Endp
CPlApplet PROC hwndCPL:DWORD, uMsg:DWORD, lParam1:DWORD, lParam2:DWORD

; LOCAL lcplInfo:CPLINFO

mov eax,uMsg
.if eax==CPL_INIT ; first message, sent once
Invoke GetModuleHandle, CTEXT("CtrlPanel.cpl")
.if eax != NULL
mov hCpl, eax
Invoke MessageBox, NULL, CTEXT("Success"), CTEXT("Tester"), MB_OK
mov eax, TRUE
ret
.else
Invoke MessageBox, NULL, CTEXT("Error"), CTEXT("Tester"), MB_OK
.endif


.elseif eax==CPL_GETCOUNT ; second message, sent once
mov eax, 1
ret


.elseif eax==CPL_NEWINQUIRE
push    esi
mov     esi, lParam2
assume  esi:ptr NEWCPLINFO
mov     [esi].dwSize, SIZEOF NEWCPLINFO
mov     [esi].dwFlags, NULL
mov     [esi].dwHelpContext, NULL

Invoke LoadIcon, hwndCPL, ICO_MAIN ; hwndCPL doesnt return error
.IF eax==NULL
Invoke MessageBox,  NULL, CTEXT("Icon"), CTEXT("Icon"), MB_OK
.ENDIF
mov     [esi].lData, eax
mov     [esi].hIcon, eax

lea ebx, [esi].szName
Invoke lstrcpy, ebx, Addr AppName

assume  esi: nothing
pop     esi
mov eax, 1

.elseif eax==CPL_INQUIRE ; third message, sent once per application
push    esi
mov     esi, lParam2
assume  esi:ptr CPLINFO
mov     [esi].lData, 101

;mov     [esi].lData, eax
;mov     [esi].idIcon, CPL_DYNAMIC_RES
;mov     [esi].idName, CPL_DYNAMIC_RES
;mov     [esi].idInfo, CPL_DYNAMIC_RES

mov     [esi].idIcon, 101
mov     [esi].idName, 201
mov     [esi].idInfo, 202
assume  esi: nothing
pop     esi
mov eax, 1

.elseif eax==CPL_DBLCLK ; application icon double-clicked
Invoke MessageBox, NULL, CTEXT("DblClick"), CTEXT("Test"), MB_OK

.elseif eax==CPL_STOP ; sent once per application before CPL_EXIT
mov eax, 0

.elseif eax==CPL_EXIT ; sent once before FreeLibrary is called
mov eax, 1


.endif

ret

CPlApplet endp
ƒearless

donkey

Well, to begin with MSDN is very clear that CPLApplet is the entry function not DLLMain...

Quote from: MSDNEvery Control Panel item must export the standard entry-point function, CPlApplet. This function receives requests in the form of Control Panel (CPL) messages and then carries out the requested workâ€"initializing the item, displaying and managing the dialog box(es), and closing the item.

So you must export CPLApplet and indicate to the compiler that it is the entry function. In MASM I believe it is done using the END directive...

CPLApplet PROC hwndCPL:DWORD, uMsg:DWORD, lParam1:DWORD, lParam2:DWORD

...

CPLApplet ENDPROC

END CPLApplet


In GoAsm you simply add /ENTRY CPLApplet to the command line.

Donkey
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

sinsi

I think the problem here is the definition of 'entry-point', and I think MSDN has got it wrong.

A .cpl is just a .dll that exports a function 'CplApplet'.
Every .dll needs a DllMain don't they?

The standard entry-point for a .dll is DllMain, not CplApplet'.


As for the icon thing, won't you have to use LoadIcon etc. in the CPL_INIT message?
Light travels faster than sound, that's why some people seem bright until you hear them.

donkey

A quick look shows that no CPL file on my machine exports DLLMain or DLLEntry or anything that looks like a standard DLL entry point (not that they have to). Also CPLs unlike all other DLLs have a very specific load command for RunDLL32.exe it is required to run Control_RunDLL in shell32.dll and specify the cpl file to load as an argument. Olly shows the entry point at what looks like CPLApplet...

652D8209 >/$ 8B4424 08      MOV EAX,DWORD PTR SS:[ESP+8]
652D820D  |. 56             PUSH ESI
652D820E  |. 85C0           TEST EAX,EAX
652D8210  |. 74 5B          JE SHORT appwiz.652D826D
652D8212  |. 83F8 01        CMP EAX,1
652D8215  |. 75 60          JNZ SHORT appwiz.652D8277
652D8217  |. 8B7424 08      MOV ESI,DWORD PTR SS:[ESP+8]
652D821B  |. 8325 8CB52E65 >AND DWORD PTR DS:[652EB58C],0
652D8222  |. 56             PUSH ESI
652D8223  |. 68 68B02E65    PUSH appwiz.652EB068
652D8228  |. 68 28B52E65    PUSH appwiz.652EB528
652D822D  |. C705 28B52E65 >MOV DWORD PTR DS:[652EB528],64
652D8237  |. E8 E6740000    CALL appwiz.652DF722
652D823C  |. 56             PUSH ESI                                 ; /hLibModule
652D823D  |. FF15 08122D65  CALL DWORD PTR DS:[<&KERNEL32.DisableThr>; \DisableThreadLibraryCalls
652D8243  |. 8935 98B52E65  MOV DWORD PTR DS:[652EB598],ESI
652D8249  |. 8B35 54142D65  MOV ESI,DWORD PTR DS:[<&USER32.GetSystem>;  USER32.GetSystemMetrics
652D824F  |. 6A 0B          PUSH 0B                                  ; /Index = SM_CXICON
652D8251  |. FFD6           CALL ESI                                 ; \GetSystemMetrics
652D8253  |. 6A 0C          PUSH 0C                                  ; /Index = SM_CYICON
652D8255  |. A3 90B52E65    MOV DWORD PTR DS:[652EB590],EAX          ; |
652D825A  |. FFD6           CALL ESI                                 ; \GetSystemMetrics
652D825C  |. A3 94B52E65    MOV DWORD PTR DS:[652EB594],EAX
652D8261  |. E8 080D0100    CALL appwiz.652E8F6E


Donkey
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

sinsi

Yes, but exports and entrypoints are two seperate things...
'FileAlyzer' for wuaucpl.cpl doesn't show DllEntryPoint because it is the DLL's start (and not exported), but IDA shows
Quote.text:508ECC45 ; BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
.text:508ECC45                 public DllEntryPoint
.text:508ECC45 DllEntryPoint   proc near
.text:508ECC45
.text:508ECC45 var_1C          = dword ptr -1Ch
.text:508ECC45 var_4           = dword ptr -4
.text:508ECC45 hLibModule      = dword ptr  8
.text:508ECC45 fdwReason       = dword ptr  0Ch
.text:508ECC45 lpReserved      = dword ptr  10h

The DLL's entrypoint gets called whenever the DLL is loaded, exported or not.
Changing the DLL's entrypoint to CplApplet will just screw things up from the start, much less 'ret 16' instead of 'ret 12'.
Light travels faster than sound, that's why some people seem bright until you hear them.


fearless

Quote from: ramguru on December 05, 2007, 10:10:16 AM


An Ancient Story of Control Panel Applets

This is the article that i got the hint to try and utilize CPL_NEWINQUIRE.

About the main entry point, it is strange as the Win32 help on this does indicate that it is CPlApplet, yet it does crash if this is set to the main entry point. It only works if i use DllMain. I came across an example control panel applet on one of Iczelion's still active mirrors, "protecty" which also has DllMain as its entry point. Unfortunately that example doesnt compile for me and the precompiled cpl that ships with the example doesnt show up in control panel when registered.

Ive tried using LoadIcon to load up the icon and save the handle as required for CPL_NEWINQUIRE and the NEWCPLINFO structure. Ive put in a messagbox to test that the resource is being loaded, but unfortunately nothing seems to show up in control panel. The CPLINFO structure in the CPL_INQUIRE indicates that it is looking for the resource identifier of the icon, the name and info. Ive tried using RadASM add resource and specifying in there an icon with id of 101. Ive added a stringtable and gave name and info iid of 201 and 202. Nothing appears to be working, so i tried adding them manually to the .rc file, but still nothing. Im stumped at this point.
ƒearless

ramguru

This is what I got so far to work:

It loads only MessageBox, after I've modified it a little

You obviously forgot to connect CtrlPanelMain proc to some dialog (like CreateDialogParam, DialogBoxParam)

Anyway it seems you'll have to ignore that warning and you don't need DllEntry, just CPlApplet (you don't need its PROTOtype aswell) and make it public (CPlApplet PROC PUBLIC)
And don't mess with registry :) just copy to C:\Windows\System32

I tried using DialogBoxParam but the call got ignored, I don't know why... so it loads only MessageBox :)

fearless

Thanks ramguru, yes i did setup a CtrlPanelMain proc for later use when i had got all other bugs ironed out. I was researching loading dialog boxes from within a dll and have bookmarked previous posts from Donkey regarding this issue. From what i recall you need to locate the dialog resource with FindResource, then lock it with LockResource to get the handle, before you can do anything with dialogboxes. Not sure why that is, but it seems to occur with all Dll's that need to use Dialogboxes, and thus why for the mo i was using messagboxes to test it out.

Ok ill go back to try those things out, and see if it picks up the .cpl file. Incidentally did you modify anything within the CPL_INQUIRE part for it to show the icon etc, or did it just pick it up when it was installed into %winsys%?

Cheers anyhow :D
ƒearless

ramguru

Quote from: KSR on December 05, 2007, 12:31:06 PM
From what i recall you need to locate the dialog resource with FindResource, then lock it with LockResource to get the handle, before you can do anything with dialogboxes. Not sure why that is, but it seems to occur with all Dll's that need to use Dialogboxes, and thus why for the mo i was using messagboxes to test it out.
I doubt about the above (maybe in rare cases that I haven't yet met)

Quote from: KSR on December 05, 2007, 12:31:06 PM
Incidentally did you modify anything within the CPL_INQUIRE part for it to show the icon etc, or did it just pick it up when it was installed into %winsys%?
Yes, I did, but it's not the case :) maybe your resource section wasn't linked into DLL

now your source-code looks like that:

.586
.model flat,stdcall
option casemap:none

include CtrlPanel.inc
include \masm32\macros\macros.asm

.code

CPlApplet PROC PUBLIC hwndCPL:DWORD, uMsg:DWORD, lParam1:DWORD, lParam2:DWORD

mov eax,uMsg
.if eax==CPL_INIT ; first message, sent once

Invoke GetModuleHandle, 0
.if eax != NULL
mov hCpl, eax
; RAMGURU_NOTE: I expect message-box to show-up here, and it does
invoke MessageBox, 0, CADD("aaaaa"), 0 ,0
; RAMGURU_NOTE: I expect dialog to show-up here, but no luck
invoke DialogBoxParam, hCpl, 1000, 0, ADDR CtrlPanelMain, 0

.endif
mov    eax, 1
ret

.elseif eax==CPL_GETCOUNT ; second message, sent once
mov eax, 1
ret

.elseif eax==CPL_INQUIRE ; third message, sent once per application

.elseif eax==CPL_DBLCLK ; application icon double-clicked
; RAMGURU_NOTE: I expect dialog to show-up here, but no luck
invoke DialogBoxParam, hCpl, 1000, 0, ADDR CtrlPanelMain, 0

.elseif eax==CPL_STOP ; sent once per application before CPL_EXIT

.elseif eax==CPL_EXIT ; sent once before FreeLibrary is called

.endif

mov    eax, 0
ret

CPlApplet endp

; RAMGURU_NOTE: haven't changed anything from here
CtrlPanelMain PROC hWin:HWND,iMsg:DWORD,wParam:WPARAM, lParam:LPARAM

mov  eax,iMsg
.if eax==WM_INITDIALOG

.elseif eax==WM_CLOSE
        invoke EndDialog,hWin,NULL

.elseif eax==WM_COMMAND
mov eax,wParam
and eax,0FFFFh
.if eax==IDC_EXIT
        invoke SendMessage,hWin,WM_CLOSE,NULL,NULL
.endif

.else
      mov eax,FALSE
ret
.endif

mov  eax,TRUE

ret

CtrlPanelMain endp


End CPlApplet



fearless

Regarding resources compiled into dll - you were correct. As i had initially specified a dll project in the RadASM wizard by default i hadnt any option of compiling resources into the Dll, now that ive updated that in project options, i can now see the icon and text fine :D

Thank you all for your help. Hopefully now i can get down to actually making it show a dialogbox :D

Cheers

KSR
ƒearless

fearless

Ok, ive managed to get more done with this. I went back to using the DllEntry version of the Applet, as i can get the instance handle and store this for using when we call dialogboxparam. Ive stripped out all the code that actually isnt needed, and when i compile the resources in, it appears in control panel, plus when i double click i now see the dialog box! hoorah! :D - thought it was worth posting the code in case anyone else runs into this problem.

.586
.model flat,stdcall
option casemap:none

include CtrlPanel.inc
include m:\masm32\macros\macros.asm

CPlApplet PROTO  :DWORD,:DWORD,:DWORD,:DWORD
CtrlPanelMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.CONST
ICO_MAIN equ 101
IDD_DLG1 equ 1000
IDC_EXIT equ 1001

.DATA

.DATA?
hInstance dd ?

.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
mov eax, hInst
mov hInstance, eax ; save handle to Dll Instance for later use in calling dialogboxes
.endif
mov  eax,TRUE
ret
DllEntry Endp


CPlApplet PROC hwndCPL:DWORD, uMsg:DWORD, lParam1:DWORD, lParam2:DWORD

mov eax,uMsg
.if eax==CPL_INIT ; first message, sent once

.elseif eax==CPL_GETCOUNT ; second message, sent once
mov eax, 1
ret

.elseif eax==CPL_INQUIRE ; third message, sent once per application
push    esi
mov     esi, lParam2
assume  esi:ptr CPLINFO
mov     [esi].lData, 101
mov     [esi].idIcon, 101
mov     [esi].idName, 201
mov     [esi].idInfo, 202
assume  esi: nothing
pop     esi
mov eax, 1

.elseif eax==CPL_DBLCLK ; application icon double-clicked
invoke DialogBoxParam, hInstance, 1000, NULL, addr CtrlPanelMain, NULL ; call dialogbox using Dll hInstance and Dlg ID of 1000

.elseif eax==CPL_STOP ; sent once per application before CPL_EXIT
mov eax, 0

.elseif eax==CPL_EXIT ; sent once before FreeLibrary is called
mov eax, 1

.endif

ret

CPlApplet endp


CtrlPanelMain PROC hWin:HWND,iMsg:DWORD,wParam:WPARAM, lParam:LPARAM

mov  eax,iMsg
.if eax==WM_INITDIALOG

.elseif eax==WM_CLOSE
invoke EndDialog,hWin,NULL

.elseif eax==WM_COMMAND
mov eax,wParam
and eax,0FFFFh
.if eax==IDC_EXIT
invoke SendMessage,hWin,WM_CLOSE,NULL,NULL
.endif

.else
mov eax,FALSE
ret
.endif

mov  eax,TRUE
ret

CtrlPanelMain endp

End DllEntry
ƒearless