You are not logged in. You can browse in the PowerBASIC Community, but you must click Login (top right) before you can post. If this is your first visit, check out the FAQ or Sign Up.
Thanks Stuart, yes sometimes the main may call up the SAME dll several times which may need to be control in some situation
I'm trying to ensure that the same dll is not called more than once as it is sometimes in certain situations we need to be able to control as we do
engineering computations programming
I have also found out that MUTEX do work for DLL , I tested run the below codes with the Main prog.exe calling 2 separate DLLs
1. if the DLL prog1.dll and DLL prog2.dll have the same mutex then DLL prog2.dll won't work
2. if the DLL prog1.dll and DLL prog2.dll have the DIFFERENT mutex then DLL prog2.dll would work
the Main Prog.bas
Code:
' Main Prog.bas
' modified from
' http://www.garybeene.com/power/tutorial/pb-tutor-dll.htm
#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"
DECLARE FUNCTION RandomAdd LIB "DLL prog1.dll" ALIAS "RandomAdd" (BYVAL x&) AS LONG
DECLARE FUNCTION RandomAdd2 LIB "DLL prog2.dll" ALIAS "RandomAdd2" (BYVAL x&) AS LONG
FUNCTION PBMAIN () AS LONG
DIM a AS LONG,b AS LONG
a = RandomAdd(b)
MSGBOX " a " + STR$(a)
' this won't work which results in u2 = 0
' if the mutex of DLL prog2.dll
' is the same as DLL prog1.dll
DIM u2 AS LONG, ub AS LONG
u2 = RandomAdd2(ub)
MSGBOX " u2 " + STR$(u2)
' hence mutex does work for dll
END FUNCTION
the DLL prog1.bas
Code:
' Dll prog1.bas
#COMPILE DLL 'directs compiler to create DLL
#DIM ALL
#INCLUDE "WIN32API.INC"
FUNCTION RandomAdd ALIAS "RandomAdd" (BYVAL x AS LONG) EXPORT AS LONG
' check mutex for ProgDlg2.exe
LOCAL chkInst AS LONG
chkinst = chkanotherInstance2()
IF chkinst = -1 THEN
' another instance exist -- exit this program
EXIT FUNCTION
END IF
RANDOMIZE TIMER
RandomAdd = x + RND * 100 'adds random amount to x
END FUNCTION
'=============================================
' this function prevents loading another
' instance of ProgDlg2.exe
'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/817778-does-the-use-of-atom-slows-down-the-loading-of-a-program?p=817780#post817780
' Great Thanks to Peter
FUNCTION chkanotherInstance2() AS LONG
' Mutex for ProgDlg2
LOCAL atmtxt AS ASCIIZ * 33
LOCAL myatom AS STRING
myatom = "m56ertmHJK-#[email protected]#"
LOCAL hmutex AS DWORD
LOCAL Numutex AS LONG
Numutex = 1553902
atmtxt = myatom + STR$(Numutex)
hMutex = CreateMutex (BYVAL %Null, 0 ,atmtxt)
IF hMutex AND (GetLastError = %ERROR_ALREADY_EXISTS) THEN
' an instance of the program already running
' we must exit this program
FUNCTION = -1
ELSE
FUNCTION = 0
END IF
END FUNCTION
the DLL prog2.bas
Code:
' Dll prog2.bas
#COMPILE DLL 'directs compiler to create DLL
#DIM ALL
#INCLUDE "WIN32API.INC"
FUNCTION RandomAdd2 ALIAS "RandomAdd2" (BYVAL x AS LONG) EXPORT AS LONG
' check mutex for ProgDlg2.exe
LOCAL chkInst AS LONG
chkinst = chkanotherInstance3()
IF chkinst = -1 THEN
' another instance exist -- exit this program
EXIT FUNCTION
END IF
RANDOMIZE TIMER
RandomAdd2 = x + RND * 100 + 3000 'adds random amount to x
END FUNCTION
'=============================================
' this function prevents loading another
' instance of ProgDlg2.exe
'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/817778-does-the-use-of-atom-slows-down-the-loading-of-a-program?p=817780#post817780
' Great Thanks to Peter
FUNCTION chkanotherInstance3() AS LONG
' Mutex for ProgDlg2
LOCAL atmtxt AS ASCIIZ * 33
LOCAL myatom AS STRING
' same myatom as DLL prog1.DLL
' myatom = "m56ertmHJK-#[email protected]#"
' different myatom from DLL prog1.DLL
myatom = "pakerghergh-#[email protected]#"
LOCAL hmutex AS DWORD
LOCAL Numutex AS LONG
Numutex = 1553902
atmtxt = myatom + STR$(Numutex)
hMutex = CreateMutex (BYVAL %Null, 0 ,atmtxt)
IF hMutex AND (GetLastError = %ERROR_ALREADY_EXISTS) THEN
' an instance of the program already running
' we must exit this program
FUNCTION = -1
ELSE
FUNCTION = 0
END IF
END FUNCTION
Your logic is confused.
Using a mutex to control the program flow in a function does not ensure that "the main program can call or load only one instance of each DLL at any one time."
Both DLLs are loaded by the applicaion when it starts and the functions in both are then available.in the application.
All your code does is prevent a function in the DLL from returning a valid value if that function or a function in another DLL (or even one in your main application) has set a specific mutex and the mutex still exists.
The DLLs are still loaded and any other function in the DLL can still be called.
It doesn't matter which DLL' s function you call first. Once you have done so, calling either of them subsequentlly will return "0" for the life of your application (until your appplication specifically releases the mutex or the application closes and the mutex becomes "abandoned".)
What you have done is essentially create a "use-once" function.
It is exactly the same as if you had put the functions in your main application instead of a DLL.
You would still only be able to call one of the functions once and subsequent call to either function would fail.
Thank you Stuart, it is true any subsequent call to DLL prog1.dll will also fail !
Is there a statement or command to delete or release a mutex after its use ? for atoms we can use Globaldeleteatom()
is there an equivalent command for mutex ?
Thank you Stuart, it is true any subsequent call to DLL prog1.dll will also fail !
Please, get this clear: You dont "call a DLL", you "load a DLL" and then "call a function in a DLL". If you want something to happen when you "load a DLL", you need to put code in the LIBMAIIN() function (look it up in Help)
Any call to the functions RandomAdd1 or RandomAdd2 will fail. If there were any other functions in the DLLs, you would be able to call them with no problem.
Is there a statement or command to delete or release a mutex after its use ? for atoms we can use Globaldeleteatom()
is there an equivalent command for mutex ?
ReleaseMutex. will delete it IF it is called by the process (application in this case) that created the mutex (has ownership) AND there is no thread or other application trying to get ownership of the mutex with a "WaitForSingleObject" or similar.
Note that it doesn't matter whether the mutex is created in functions in the DLLs or functions in your main application. The mutex is owned by your application's main thread ( process) so you can release it from any function in the main application (including PBMain() ) or a DLL..
Thank you Dale and Stuart, I have given up on Mutex as it is a hard beast to handle, so I check for the existence of a loaded DLL using FindWindow() which
is more efficient, the below program illustrate this method.
I have tried using LIBMAIN() and Mutex and it doesn't work out, even if we exit the DLL and do a ReleaseMutex()
it doesn't work
MyMain.exe program
Code:
' MyMain.bas
' https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/766076-subclass-dialog-from-dll?p=766170#post766170
#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"
%IDD_DIALOG1 = 101
%IDC_BUTTON1 = 1001
'GLOBAL FlagRan AS long
DECLARE FUNCTION startdll LIB "WinDisp-dll.dll" ALIAS "initdll" (BYVAL xx AS LONG) AS LONG
FUNCTION PBMAIN() ' ** Main Application Entry Point **
ShowDIALOG1 %HWND_DESKTOP
END FUNCTION
'------------------------------------------------------------------------------
CALLBACK FUNCTION ShowDIALOG1Proc() ' ** CallBacks **
LOCAL lblTEXT AS ASCIIZ * 20
LOCAL lRes AS DWORD
LOCAL hDLL AS DWORD
SELECT CASE AS LONG CBMSG
CASE %WM_COMMAND
SELECT CASE AS LONG CBCTL
CASE %IDC_BUTTON1
DIALOG SET TEXT CB.HNDL, HEX$(GetActiveWindow) ' test
' IF hMutex THEN
' ReleaseMutex(hMutex)
' END IF
hDLL = FindWindow("","WinDisp-dll" )
IF hDLL = 0 THEN
' if FlagRan = 0 then
lRes = startdll( GetActiveWindow() ) ' Send active hWnd ' should be Dialog1..
' if lRes then
' FlagRan = 1
' end if
END IF
' If lRes Then Control Disable Cb.Hndl, %IDC_BUTTON1
END SELECT
END SELECT
END FUNCTION
'------------------------------------------------------------------------------
FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG ' ** Dialogs **
LOCAL lRslt AS LONG
LOCAL hDlg AS DWORD
DIALOG NEW PIXELS, hParent, "Dialog1", 70, 70, 301, 221, _
%WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR _
%WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX TO hDlg
CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Start WinDisp-dll", 85, 30, 100, 20
DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
FUNCTION = lRslt
END FUNCTION
WinDisp-dll.DLL program .
Code:
' WinDisp-dll.bas
#COMPILE DLL
#DIM ALL
#INCLUDE "Win32API.inc"
%IDC_Button = 500
%IDC_Button1= 501
GLOBAL hWndMain AS DWORD
GLOBAL ghWndInst AS DWORD
GLOBAL gOldProc AS DWORD
' GLOBAL hmutex AS DWORD
'-------------------------------------------------------------------------------
' Main DLL entry point called by Windows...
'
FUNCTION LIBMAIN (BYVAL hInstance AS LONG, _
BYVAL fwdReason AS LONG, _
BYVAL lpvReserved AS LONG) AS LONG
SELECT CASE fwdReason
CASE %DLL_PROCESS_ATTACH
'Indicates that the DLL is being loaded by another process (a DLL
'or EXE is loading the DLL). DLLs can use this opportunity to
'initialize any instance or global data, such as arrays.
FUNCTION = 1 'success!
'FUNCTION = 0 'failure! This will prevent the EXE from running.
CASE %DLL_PROCESS_DETACH
'Indicates that the DLL is being unloaded or detached from the
'calling application. DLLs can take this opportunity to clean
'up all resources for all threads attached and known to the DLL.
' if hMutex then
' ReleaseMutex(hMutex)
' end if
FUNCTION = 1 'success!
'FUNCTION = 0 'failure!
CASE %DLL_THREAD_ATTACH
'Indicates that the DLL is being loaded by a new thread in the
'calling application. DLLs can use this opportunity to
'initialize any thread local storage (TLS).
FUNCTION = 1 'success!
'FUNCTION = 0 'failure!
CASE %DLL_THREAD_DETACH
'Indicates that the thread is exiting cleanly. If the DLL has
'allocated any thread local storage, it should be released.
' IF hMutex THEN
' ReleaseMutex(hMutex)
' END IF
FUNCTION = 1 'success!
'FUNCTION = 0 'failure!
END SELECT
END FUNCTION
FUNCTION initdll ALIAS "initdll"(BYVAL hInst AS LONG ) EXPORT AS LONG
LOCAL hwnd AS DWORD
LOCAL lpCmdLine AS ASCIIZ PTR
' check mutex for WinDisp-dll.dll
' LOCAL chkInst AS LONG
' chkinst = chkanotherInstance2()
' IF chkinst = -1 THEN
' another instance exist -- exit this dll
' EXIT FUNCTION
' END IF
ghWndInst = hInst ' Global handle to Dialog1 (Target?)
hwnd = GetParent(hwnd) ' ??
WINMAIN hwnd, 0, lpCmdLine, %SW_SHOW
FUNCTION = ghWndInst
END FUNCTION
'=============================================
' this function prevents loading another
' instance of ProgDlg2.exe
'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/817778-does-the-use-of-atom-slows-down-the-loading-of-a-program?p=817780#post817780
' Great Thanks to Peter
FUNCTION chkanotherInstance2() AS LONG
' Mutex for ProgDlg2
LOCAL atmtxt AS ASCIIZ * 33
LOCAL myatom AS STRING
LOCAL hmutex AS DWORD
myatom = "m56ertmHJK-#[email protected]#"
LOCAL Numutex AS LONG
Numutex = 1553902
atmtxt = myatom + STR$(Numutex)
hMutex = CreateMutex (BYVAL %Null, 0 ,atmtxt)
IF hMutex AND (GetLastError = %ERROR_ALREADY_EXISTS) THEN
' an instance of the program already running
' we must exit this program
FUNCTION = -1
ELSE
FUNCTION = 0
END IF
END FUNCTION
FUNCTION WINMAIN (BYVAL hInst AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpCmdLine AS ASCIIZ PTR, BYVAL iCmdShow AS LONG) AS LONG
'arguments to WinMain are provided by Windows - not defined by the programmer
'hInst current instance of the application
'hPrevInstance previous instance (not used - returns %Null in Win32)
'lpCmdLine pointer to command line (whatever the user types in after MyApp.EXE). A null terminated string.
'iCmdShow used the first time ShowWindow is called to display the application main window
LOCAL Msg AS tagMsg
LOCAL W AS WndClassEx
LOCAL szAppName AS ASCIIZ * 80
LOCAL hWndButton AS DWORD
LOCAL hAccel AS DWORD
LOCAL WndStyle AS LONG
LOCAL WndStyleX AS LONG
LOCAL hWnd,hInstance AS DWORD
'register window class
szAppName = "SDK"
W.cbSize = SIZEOF(W)
W.Style = %CS_HREDRAW OR %CS_VREDRAW '%WS_OverlappedWindow Or %WS_HScroll Or %WS_VScroll '
W.lpfnWndProc = CODEPTR(WndProc)
W.cbClsExtra = 0
W.cbWndExtra = 0
W.hInstance = hInst
W.hIcon = %null 'LoadIcon(hInst, "logo")
W.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
W.hbrBackground = %Color_BtnFace+1
W.lpszMenuName = %NULL
W.lpszClassName = VARPTR(szAppName)
W.hIconSm = LoadIcon(hInst, BYVAL %IDI_APPLICATION) 'why not %NULL
RegisterClassEx W
'create window of that class
WndStyle = %WS_OVERLAPPEDWINDOW
WndStyleX = %WS_EX_LEFT
hWndMain = CreateWindowEX( _
WndStyleX, _ 'dwStyle extended style of window
szAppName, _ 'lpClassName pointer to a null-terminated string (any registered name, including predefined system class names).
"Child dialog", _ 'lpWindowName pointer to a null-terminated String (placed in title bar, text of the control, icon name or #identifier)
WndStyle, _ 'dwStyle style of window
500, _ 'x initial horizontal position (upper left screen, except for child is relative to parent window's client area)
200, _ 'y initial vertical position (upper left screen, except for child is relative to parent window's client area)
300, _ 'nWidth width in device units
300, _ 'nHeight height in device units
%Null, _ 'hWndParent handle to parent window (optional for a pop-up window)
%Null, _ 'hMenu handle to a menu
hInst, _ 'handle to instance of the module to be associated with the window
BYVAL %Null _ 'pointer to CREATESTRUCT structure pointed to by the lParam param of the WM_CREATE message.
)
'create window of existing class "Button"
WndStyle = %WS_TABSTOP OR %WS_VISIBLE OR %WS_CHILD OR %BS_DEFPUSHBUTTON
hInstance = GetWindowLong(hWndMain, %GWL_HINSTANCE)
hWnd= CreateWindow( "Button", "Send to Parent", WndStyle, 10, 10, 110, 25, hWndMain, %IDC_Button , hInstance, BYVAL %Null )
hWnd= CreateWindow( "Button", "Get Parent size", WndStyle, 10, 60, 160, 25, hWndMain, %IDC_Button1, hInstance, BYVAL %Null )
ShowWindow hWndMain, iCmdShow 'controls how window is to be shown. 1st must use iCmdShow
UpdateWindow hWndMain 'sends the window it's first WM_Create to display the window on the screen
'message pump - calls WndProc whenever an application-specific message is received
'WndProc can process the message, or pass it on to a the default window procedure that is built into Windows.
WHILE GetMessage(Msg, %NULL, 0, 0) > 0
IF ISFALSE TranslateAccelerator (hWndMain, hAccel, Msg) THEN
IF ISFALSE ISDialogMessage (hWndMain, Msg) THEN
TranslateMessage Msg
DispatchMessage Msg
END IF
END IF
WEND
END FUNCTION
FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) EXPORT AS LONG
LOCAL hDC AS DWORD, pPaint AS PAINTSTRUCT, tRect AS RECT
LOCAL pnmhdr AS NMHDR PTR
LOCAL oldProc AS DWORD
LOCAL lblTEXT AS ASCIIZ * 20
LOCAL DialogID AS LONG
SELECT CASE wMsg
CASE %WM_CREATE
' Subclass
' DialogID = getdlgctrlid ( ghWndInst )
' oldProc = SetWindowLong( DialogID, %GWL_WNDPROC, CODEPTR(SubClassProc))
goldProc = SetWindowLong( ghWndInst, %GWL_WNDPROC, CODEPTR(SubClassProc)) ' SubClass Dialog1 ??
'SetTimer hWnd, 1001, 500, 0
'SendMessage hwnd,%WM_TIMER, 1001, 0
CASE %WM_DESTROY
' SetWindowLong DialogID, %GWL_WNDPROC, oldProc
' ReleaseMutex(hMutex)
SetWindowLong ghWndInst, %GWL_WNDPROC, goldProc
PostQuitMessage 0
EXIT FUNCTION
CASE %WM_COMMAND
'control notifications
SELECT CASE LO(WORD,wParam)
CASE %IDC_Button
SELECT CASE HI(WORD,wParam)
CASE %BN_CLICKED
lblTEXT = "From child " & TIME$ ' Send message to Parent
setWindowText( ghWndInst, lblTEXT )
END SELECT
CASE %IDC_Button1
' Get size from Parent
SELECT CASE HI(WORD,wParam)
CASE %BN_CLICKED
GetClientRect ghWndInst, tRect ' get size from parent client
lblTEXT= STR$(trect.left) & "|" & STR$(trect.top) & "|" & _
STR$(trect.right-trect.left) & "|" & STR$(trect.bottom-trect.top)
setWindowText( ghWndInst, lblTEXT )
END SELECT
END SELECT
CASE %WM_SIZE
'after its size has changed on local dialog
' lblTEXT= "local size "& STR$(Lo(WORD,lParam)) & "|" & STR$(Hi(WORD,lParam)) 'STR$(wParam)
lblTEXT= "WinDisp-dll"
setWindowText( hWnd, lblTEXT )
CASE %WM_ERASEBKGND
hDC = wParam
DrawGradient hDC
FUNCTION = 1
EXIT FUNCTION
CASE %WM_PAINT
'set when request is made to paint a portion of an application's window.
hDC = BeginPaint(hWnd, pPaint)
GetClientRect hWnd, tRect
SetBkMode hDC, %TRANSPARENT
SetTextColor hDC,%RGB_BLUE '%WHITE
DrawText hDC, "Child", -1, tRect, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
EndPaint hWnd, pPaint
FUNCTION = 1
EXIT FUNCTION
CASE %WM_TIMER
' SELECT CASE WPARAM
' CASE 1001
lblTEXT= "Timer " & TIME$ & STR$(LO(WORD,lParam)) & "|" & STR$(HI(WORD,lParam)) 'STR$(wParam)
setWindowText( hWnd, lblTEXT )
' IF ghWndInst > 0 THEN
' GetClientRect ghWndInst, tRect
'SendMessage(hWnd, %WM_SIZE, wparam, lParam)
' movewindow( hwnd, trect.left, trect.top, trect.right-trect.left, trect.bottom-trect.top, -1 )
' end if
' end select
END SELECT
FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam) 'if not handled above, pass to Windows default message handler.
END FUNCTION
'======================== SUBCLASS ============================================
FUNCTION SubClassProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
LOCAL lRes, oldProc AS DWORD
LOCAL lblTEXT AS ASCIIZ * 20
SELECT CASE AS LONG wMsg
CASE %WM_COMMAND
' SELECT CASE LO(WORD,wParam)
lblTEXT= TIME$ + STR$(wParam)
setWindowText( hWnd, lblTEXT )
FUNCTION = 0 : EXIT FUNCTION
' end select
CASE %WM_SIZE 'after its size has changed.
lblTEXT= "SIZE of PARENT " 'STR$(wParam)
setWindowText( hWnd, lblTEXT )
END SELECT
FUNCTION = CallWindowProc(goldProc, hWnd, wMsg, wParam, lParam)
END FUNCTION
'------------------------------------------------------------------
SUB DrawGradient (BYVAL hDC AS DWORD)
LOCAL rectFill AS RECT, rectClient AS RECT, fStep AS SINGLE, hBrush AS DWORD, lOnBand AS LONG
GetClientRect WindowFromDC(hDC), rectClient
' fStep = rectClient.nbottom / 200
' the more steps the finer is the color gradient
fStep = rectClient.nbottom / 156
FOR lOnBand = 0 TO 155
SetRect rectFill, 0, lOnBand * fStep, rectClient.nright + 1, (lOnBand + 1) * fStep
' creates a yellowish greenish background
hBrush = CreateSolidBrush(RGB(221, 250, 180 - lOnBand))
Fillrect hDC, rectFill, hBrush
DeleteObject hBrush
NEXT
END SUB
so that when the main program can call or load only one instance of each DLL at any one time.
Windows will control any physical loading to your address space. If the DLL is already present it returns a handle. If not, Windows will load it and return a handle. Either way, you get a handle. The libray itself is loaded in Windows' space and all that ever goes in your user space is a handle.
As per above, this is not really what you are talking about but you need to know this.
Even if you have multiple programs loading the library the library itself will be only be resident once and each process gets its own handle.
Pray what are you trying to do? This description:
I'm trying to ensure that the same dll is not called more than once as it is sometimes in certain situations we need to be able to control as we do
engineering computations programming
... really does not make sense, as DLLs are not called... procedures are called. Are you worried about data integrity? Possible re-entrancy? You should not be worried about either.
WTF? Calling a WInMain function in a DLL function? Read Help on WInMain!!! "The WINMAIN function is called by Windows when an executable application first loads and begins to run."
That whole heap of garbage indicates that you may be able to copy and paste, but have no idea what you are doing.
There is no point in trying to help you further.
I don't understand what I'm doing wrong. I want to reach all events from dialog1(main), I have tried this with a SUBCLASS of dialog1 to be able to capture this in a
In that linked post, the WINMAIN function is a user-written (and named) function in a code module specified as [HASHTAG="t493"]compile[/HASHTAG] DLL.
'WINMAIN' here is, in my opinion, the worst possible name for a user-written function in the history of functions. All it can do is confuse PB users. (I'd say it worked pretty well!)
Regardless, I really suggest you brush up on "entry point functions" when compiling for either EXE or DLL.
Naming an entry point into either an executable file OR a DLL is a matter of convention. For an executable file, the normal PBMAIN is where the executable code begins. You hunt up the instance handle with an API if you need it and then write your UI code after that. A DLL is a different animal, it requires a specific format that is defined by the operating system and in the case of PowerBASIC, its a PB specific wrapper to ensure that PB is supported properly. A DLLMAIN style of entry point is more powerful than the simpler version in that it allows you to control a number of factors in the DLL.
A DLL is more complicated than an executable as it can be loaded at different addresses in a calling applications address space.
You hunt up the instance handle with an API if you need it and then write your UI code after that.
Or use WINMAIN and get the instance handle without hunting.
I have renamed the WinMain() to DLLMain() and now it works well too
DO NOT RENAME IT TO DLLMAIN, your DLL already has LIBMAIN.
Exe files get one main; either PBMain or WinMain.
DLLs files get one main; PBLibMain, LibMain or DLLMain.
Help describes the differences, but you're just copying and pasting in-the-blind aren't you?
We process personal data about users of our site, through the use of cookies and other technologies, to deliver our services, and to analyze site activity. For additional details, refer to our Privacy Policy.
By clicking "I AGREE" below, you agree to our Privacy Policy and our personal data processing and cookie practices as described therein. You also acknowledge that this forum may be hosted outside your country and you consent to the collection, storage, and processing of your data in the country where this forum is hosted.
Comment