
Introduction
Workers of the one of our division departments installed an application that used MSDE as a database server. That application was constantly hanging up with no visible reason until we finally found out that the cause of such strange behavior was the too quickly growing transaction log. After digging the MSDN, I found that shrinking transaction log for MSDE/SQL Server 2000 required 2 simple commands (see KB272318):
BACKUP LOG %DB_NAME% WITH TRUNCATE_ONLY
DBCC SHRINKFILE (%LOG_NAME%, %LOG_SIZE%)
But as the workers of that department did not have the slightest idea of what MSDE is and how to run these commands with osql utility, they asked me to write a simple wizard application to ease their job. The first version I wrote used MFC, but the second one I implemented with WTL to avoid dragging the mfc dll that is 1 MB in size, and statically linking to C runtime. I could not minimize CRT use in ATL because C++ exception support was required.
So what does this wizard allow to do?

It allows to:
- Shrink SQL Server database transaction log(s).
- Backup database after shrinking it.
- Create SQL Server job that automatically shrinks transaction logs when they reach their limit size.
- See the final results, i.e. sizes of the transaction logs after shrinking.
- Show SQL Server Enterprise Manager user interface elements as its own user interface elements.
Points of Interest
You can use this wizard as a sample for different tasks you can perform using SQL-DMO and SQL-NS. Each page of the wizard is going in a separate section and is a sample for:
Login Page

- (SQL-DMO) Listing network available SQL Servers.
- (SQL-DMO) Starting SQL server if it is stopped.
- (SQL-DMO) Connecting to SQL server using either Windows or SQL Server authentication.
Database Selection Page

- (SQL-DMO) Listing SQL Server databases, excluding system ones if needed.
- (SQL-NS) Displaying database properties (if SQL-NS is installed).
- (SQL-DMO) Listing SQL Server database transaction log(s).
- (SQL-DMO) Retrieving the properties of the transaction log file(s).
Backup Page

- Formatting backup media set properties (drag and drop is also supported).
- (SQL-DMO) Verifying the backup media set before backup.
Shrink Page

- (SQL-DMO) Starting Job Server (SQL Server Agent).
- (SQL-DMO) Creating SQL Server Jobs and their steps.
- (SQL-DMO) Creating SQL Server Alerts that trigger the jobs created.
- (SQL-DMO) Starting SQL Server job (commented).
- (SQL-DMO) Shrinking transaction log(s).
- (SQL-NS) Backing up the selected database using SQL Server Enterprise Manager UI.
- (SQL-DMO) Backing up the selected database.
- (SQL-DMO) Handling Backup Object events (PercentComplete, Complete and NextMedia events) using event sinks. (See
IBackupSink source code for more details).
- (SQL-DMO) Verifying backup media set files after backup is complete.
Miscellaneous

- Executing SQL Server Service Manager.
- Executing SQL Server Enterprise Manager.
- Trimming CRT and global heap memory.
- Displaying COM errors.
- Displaying Win32 errors.
Implementation
I will not go into deep details because the source code is self-explanatory. Just a few words:
This wizard uses Microsoft SQL-DMO (SQL Distributed Management Objects) and SQL-NS (SQL Namespace API) to perform its tasks. SQL-DMO allows applications written in languages that support Automation or COM to perform all functions performed by SQL Server Enterprise Manager.
SQL-NS allows applications written in languages that support Automation or COM to include parts of the SQL Server Enterprise Manager user interface in their own user interface.
See the image below. SQL-NS is layered on SQL-DMO.

Some years ago, I wrote a Visual Basic application, that converted the Microsoft Access databases to SQL Server ones using SQL-DMO (we had problems with SQL scripts and SQL Server import wizards because of cyclic relations).
I recalled that the only thing I had to do to use the SQL-DMO objects was to add a reference to its type library from the VB project. But starting with VC 5.0, we have compiler built-in COM support - just by using the #import preprocessor directive, the compiler reads a type library and generates C++ wrapper classes and smart pointers for COM interfaces. So we get the ease of VB and the power of C++. I mention that, because the majority of complaints is that almost all the samples are implemented with VB - but conversion of them to C++ is just a game. Besides, using compiler COM support allows us to use the same code both in MFC and ATL and without any framework at all (the old MFC project is also available for download). Just see the sample code.
Handling SQL-DMO events is just a bit more complicated. The SQL-DMO Backup, BulkCopy, Replication, Restore, SQLServer, and Transfer objects are connectable COM objects, supporting callback to the client application. We have to implement functions to handle callbacks from the server, called a sink. Using the IConnectionPoint interface, the client notifies the server of its ability to handle callbacks, providing its sink implementation as an argument. The client-implemented sink is a COM object. As with any COM application development task, implementing a sink for any SQL-DMO connectable object is fairly painless when using C++. The client application defines a class, inheriting from a defined SQL-DMO sink interface definition, then implements members to handle the callbacks of interest. The IBackupSink and Shrink Page (CWizPgShrink) source code illustrate implementation for a COM object that can handle backup object events (you can also use _COM_SMARTPTR_TYPEDEF macro to generate IConnectionPointContainer and IConnectionPoint smart pointers).
Use of SQL-NS is also implemented through the #import directive to import the type library and generating C++ wrapper classes and smart pointers for its COM interfaces. See the sample code.
Sample code (SQL-DMO)
Here is a sample code that shrinks "Northwind" database transaction log(s):
static LPCTSTR spszDbs = _T( "[Northwind]" );
const LONG lLogSize = 2;
try
{
SQLDMO::_Backup2Ptr spBackup2 = NULL;
_CREATEINSTANCE( spBackup2, SQLDMO::Backup2 );
spBackup2->Database = (LPCTSTR)spszDbs;
spBackup2->Initialize = (VARIANT_BOOL)( TRUE );
spBackup2->Action =
(SQLDMO::SQLDMO_BACKUP_TYPE)SQLDMO::SQLDMOBackup_Log;
spBackup2->TruncateLog =
(SQLDMO::SQLDMO_BACKUP_LOG_TYPE)SQLDMO::SQLDMOBackup_Log_TruncateOnly;
spBackup2->SQLBackup( g_spSqlServer2 );
SQLDMO::_Database2Ptr spDatabase2 =
g_spSqlServer2->Databases->Item( (LPCTSTR)spszDbs );
SQLDMO::LogFilesPtr spLogFiles =
spDatabase2->TransactionLog->LogFiles;
SQLDMO::_LogFile2Ptr spLogFile2 = NULL;
LONG lCount = spLogFiles->Count;
for( LONG l = 1; l <= lCount; l++ )
{
spLogFile2 = spLogFiles->Item( l );
if( spLogFile2->Size > lLogSize )
spLogFile2->Shrink( lLogSize,
SQLDMO::SQLDMOShrink_Default );
}
}
catch( IN _com_error e )
{
}
Sample code (SQL-NS)
Here is a sample code that shows "Northwind" database properties just like the SQL Server Enterprise Manager does:

static LPCTSTR spszDbs = _T( "[Northwind]" );
try
{
LONG lServer = g_spSqlNamespace->GetRootItem();
LONG lDatabases = spSqlNamespace->GetFirstChildItem( lServer,
SQLNS::SQLNSOBJECTTYPE_DATABASES, (LPCTSTR)NULL );
LONG lDatabase = spSqlNamespace->GetFirstChildItem( lDatabases,
SQLNS::SQLNSOBJECTTYPE_DATABASE, (LPCTSTR)spszDbs );
SQLNS::SQLNamespaceObjectPtr spSQLNSNamespaceObj =
g_spSqlNamespace->GetSQLNamespaceObject( lDatabase );
spSQLNSNamespaceObj->ExecuteCommandByID( SQLNS::SQLNS_CmdID_PROPERTIES,
(LONG)(HWND)NULL, SQLNS::SQLNamespace_PreferModeless );
}
catch( IN _com_error e )
{
}
Global Functions
Known Bugs
- My
CMenuButton (see picture) does not respond to the reflected messages. I have to send BN_CLICK to it manually.
- If there is no space below the
CMenuButton to show its menu, it pops menu up but the button becomes obscured by it.
- If you shrink transaction log on a remote server, the backup is not supported (it gives path errors). I must try to use temporary backup devices to see if the problem persists.
- Verifying backup media set before backup sometimes gives an error (try to add two files of the same media set and one new). I do not like that piece of code at all. And do not like the previous implementation even more (it is commented). If someone has any idea of improving that, I'll be very grateful to him.
Unknown Bugs
This is my first application in WTL. Beware of numerous unknown and other types of bugs :) All improvements, optimizations and fixes are only welcomed.
Project Dependencies
Program Dependencies
- Microsoft SQL-DMO 8.0
- Microsoft Windows 2000 or later (Windows 9x is not supported)
Project Dependency Notes
I have removed the rcstamp.exe (tool for increasing project build number) from the project to decrease its size. If you want to automatically increase the project build number, download the RCStamp Tool, copy it into the solution's bin subdirectory and add the following lines to the project post-build step:
For VC 6.0: "$(WkspDir)\bin\RCStamp.exe" "$(WkspDir)\$(TargetName) \$(TargetName).rc" *.*.+.*
For VC 7.0 and later: "$(SolutionDir)\bin\RCStamp.exe" "$(ProjectDir)$(TargetName).rc" *.*.+.*
This project was originally developed with VS.NET 2003 and then converted to VC 6.0 manually and to VS.NET 2002 using Utility to Convert VS.NET 2003 Project Files. Original VS.NET 2003 solution files are named to tlsw.sln.2003 and tlswa.vcproj.2003 - you can rename them back.
Program Setup
The setup inf file is also included in the project. You can build a small setup exe with IExpress utility. IExpress is included with Microsoft Internet Explorer Administration Kit - it allows to build small and fast setup packages. My setup inf file copies application executable and help-files to the Windows system folder (as far as this is an administrative utility), creates shortcuts to them in the [Start Menu]>[Programs]>[Company Name]>[Transaction Log Shrink Wizard] directory, and registers the information required for the uninstall (you can remove it from the [Add/Remove Programs] in the Control Panel). NOTE: SQL-DMO and SQL-NS setup script is not included. Creating setup inf files is slightly off-topic and I'll try to cover that subject in another article.
Acknowledgements
- RC Stamp tool for increasing project build number by peterchen.
- Thanks to Robert Edward Caldecott for his great idea of using Marlett font for displaying glyphs. I found that tip in MSDN (Creating Wizards) only after downloading his article code (see the wizard intro page bullets).
- Self-Centering WTL Property Sheet by same Robert Edward Caldecott.
- WTL bugs by Paul Bludov.
- Article WTL for MFC programmers by Michael Dunn.
- Ten WTL tips and tricks by Ed Gadziemski.
- A Utility to Convert VS.NET 2003 Project Files by dacris.
History
- 19-Apr-2004 - Posted the article.
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hello, I'm running the shrink program but during execution I get following error: OpenService() returned error 1060, 'The specified service does not exist as an installed service
Regards
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
EZManage SQL Pro provids 5 time faster sql backup using VDI and sharinking my logs http://www.futureitsoft.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi.
Great little application.
For us though we are looking for something that will do this to ALL databases on the server. Our is a hosting server with many databases, logs tend to get out of control. We run all DBs in Full recovery mode however even though we backup both databases and logs daily we still get large logs.
This program would be great if we could select ALL databases instead of just one.
Regards Dave www.apexhost.com.au
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Cannot e-mail you privately in reply so posting a comment.
This is a sample application for the developers. It is just a so-called "tutorial" on SQL-DMO. Any of your C/C++ programmers can suit it for your needs. However if you need some extended version of it I can help you later because now I am out of time working on commercial project. So you need to wait until I am not so busy.
#define __ARMEN_H__
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I also faced the same problem of Growing Trasaction log file. but now i shrinked the file manually by running the commands. but can you tell me how can i accomodate the shrinking of file in my database so that it happenns after a particular amount of time or size. Thanks in advanse
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Was trying to figure out why I kept getting transaction log errors despite increasing the size again and again.
This little number fixed all my issues
Great file!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This tool was just what the doctor ordered. I mean the doctor that would have worked on me becasue I was ready to jump off of the second floor of my building trying to perform a simple log file shrink in sql2005. I can't believe that MS has created such a nightmare in SQL2005.
|
| Sign In·View Thread·PermaLink | 3.22/5 |
|
|
|
 |
|
 |
Great little tool I will incorporate into my daily SQL management routine. Well designed, keep up the good work!!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
How to generate the sql script of particular database using sqldmo object through vb.net/c#.net?
I need the code.
Please help me............
Chandra Sekhar V
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Thanks  Massimo Colurcio | 6:54 12 Nov '06 |
|
|
 |
|
 |
Hi Here we have so many databases in SOL server 2000.This is my database server Now we are manually using this command DBCC SHRINKFILE (POS_LOG, 2) Where POS is the Database name so it will reduce the ldf file size I want to reduce the size of all the ldf files available in my sql server (all databases) for very week .is it possible?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
 |
It is beyond GREAT! I am "1" week new to a SQL DBA position. I was able to shrink my log file from 53GB to only 42MG with the greatest of ease. Thanks so much
Trish
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It worked great. I'm a novice DBA and tried following microsofts article but kept getting an error that the file wasn't found. Your program worked perfect to clear > 80 GB from my out of control log files.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I must have gone through hundreds of articles, tried sample scripts, got quoted $350.- by a local DBA while trying to shrink our monster log file and thanks to the Google Gods I finally ended up on your page.
The utility seemed to have worked flawlessly and freeded up 40GBs of space on our SQL server and using the wizard was as easy as it gets.
Thank You again and again and again for sharing this utility and allowing us greenhorns to use it.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Here, from Stephen Bate:
Advanced Wizard v2.0
http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=C1123F35-A139-4F55-B1E7-75B7E02755C3
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
Hello there
Nice application indeed (thanks for sharing), BUT – I have a problem when trying to access any SQL Server in our domain, other than my own / local.
Tried both in the MFC & the WTL versions, tried with Windows Authentication and / or SQL Authentication – nothing helps – I get an Access denied message
But I can access any of these servers from Enterprise Manager on my machine…
I must be doing something wrong, hope someone can give me a hand
Cheers Alex
|
| Sign In·View Thread·PermaLink | 4.00/5 |
|
|
|
 |
|
 |
Do not have access to other domain with SQL Server instance running so cannot test this case. Try to specify "Domain Name\SQL Server Instance Name" in the logon page, click next and let me know
#define __ARMEN_H__
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Armen
Will appreciate it if you could give me an example of how to do what you are suggesting
Also, I tried the code sample on the web in the article - the section where you show us how to perform the Sample code (SQL-DMO) In that sample however - the member / object g_spSqlServer2 must be first initiated - but since I am new to this, I don't manage to figure out from the project itself, how to go about it, so a more complete example can help - that is do all the steps that need to happen BEFORE the code sample for this code sample to work by itself
Hope you can help me
Thanks Alex
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Dear Alex
Start the application, goto the 2-nd (login) page - see the sample image here[^], and in the "SQL Server" combo type "Desired Domain Name\Desired SQL Server Instance Name" and let me know. What about instanciating g_spSqlServer2 in the article sample, then it is just a piece of source code placed there. For complete source, download the zip with it (link at the top).
#define __ARMEN_H__
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello again
Below is the code I cut / paste from your sample program, and hard coded a few things - to make it simple, but when I execute the code it does not fail, but it does NOT perform the backup either... Will appreciate if you could find out and tell me, what am I doing wrong here?
Thanks again Alex
static LPCTSTR spszDbs = _T("MFINCOMP"); const LONG lLogSize = 1;
SQLDMO::_SQLServer2Ptr spSqlServer2; SQLDMO::_Backup2Ptr spBackup2 = NULL;
if(SUCCEEDED(spBackup2.CreateInstance(__uuidof(SQLDMO::Backup2)))) { if(SUCCEEDED(spSqlServer2.CreateInstance(__uuidof(SQLDMO::SQLServer)))) { spSqlServer2->LoginSecure = (VARIANT_BOOL)( !FALSE); spSqlServer2->LoginTimeout = 20; // Timeout is 20 seconds spSqlServer2->ODBCPrefix = (VARIANT_BOOL)( FALSE );
if(spSqlServer2->Connect((LPCTSTR)_T("(local)"), (LPCTSTR)"sa", (LPCTSTR)"sa") == S_OK) { DoEvents();
if( spSqlServer2->VersionMajor >= 8L ) // MSDE/SQL Server 2000 { spSqlServer2->AutoReConnect = (VARIANT_BOOL)( TRUE ); spSqlServer2->AutoStart = (VARIANT_BOOL)( TRUE ); } else spSqlServer2->DisConnect();
SQLDMO::JobServerPtr spJobServer = spSqlServer2->JobServer; SQLDMO::SQLDMO_SVCSTATUS_TYPE eSST = SQLDMO::SQLDMOSvc_Unknown; eSST = spJobServer->Status;
if( eSST != SQLDMO::SQLDMOSvc_Running && eSST != SQLDMO::SQLDMOSvc_Starting ) spJobServer->Start();
SQLDMO::_JobPtr spJob = NULL; TCHAR szJobName[ 128 ] = _T("Transaction Log Shrink Wizard: Shrink MFINCOMP log");
try { spJob = spJobServer->Jobs->Item( szJobName ); spJobServer->RemoveJobByID( spJob->JobID ); } catch( IN _com_error e ) { }
DoEvents(); ::Sleep( 1000 );
if(SUCCEEDED(spJob.CreateInstance(__uuidof(SQLDMO::Job)))) { spJob->Name = szJobName; spJob->Category = _T( "Database Maintenance" );
spJobServer->Jobs->Add( spJob ); spJobServer->Jobs->Refresh();
spJob->BeginAlter();
#define MAX_SQL_STR 128 try { TCHAR szCmd1Text[ 512 ] = { 0 }; TCHAR szCmd2Text[ 512 ] = { 0 }; TCHAR szCmd1Name[ MAX_SQL_STR ] = { 0 }; TCHAR szCmd2Name[ MAX_SQL_STR ] = { 0 }; TCHAR szCmd3Name[ MAX_SQL_STR ] = { 0 }; static LPCTSTR spszCmd1Text = _T( "DBCC SHRINKFILE( [%s], 2 )" ); static LPCTSTR spszCmd2Text = _T( "BACKUP LOG [%s] WITH TRUNCATE_ONLY" );
static LPCTSTR spszCmd1Name = _T( "Pre shrink '%s'" ); static LPCTSTR spszCmd2Name = _T( "Backup '%s' truncate only" ); static LPCTSTR spszCmd3Name = _T( "Post shrink '%s'" );
LONG lStepID = 0; LONG lLogCount = 1; INT iCount = 1;
TCHAR szLogName[ MAX_SQL_STR ] = { 0 };
for( INT i = 0; i < iCount; i++ ) { ::wsprintf( szCmd1Text, spszCmd1Text, _T("MFINCOMP_Log") ); ::wsprintf( szCmd2Text, spszCmd2Text, _T("MFINCOMP") );
LPCTSTR ppszCmdText[ 3 ] = { szCmd1Text, szCmd2Text, szCmd1Text };
::wsprintf( szCmd1Name, spszCmd1Name, szLogName ); ::wsprintf( szCmd2Name, spszCmd2Name, szLogName ); ::wsprintf( szCmd3Name, spszCmd3Name, szLogName );
LPCTSTR ppszCmdNames[ 3 ] = { szCmd1Name, szCmd2Name, szCmd3Name };
for( LONG lStep = 1; lStep <= 3; lStep++ ) { SQLDMO::_JobStepPtr spJobStep = NULL;
if(SUCCEEDED(spJobStep.CreateInstance(__uuidof(SQLDMO::JobStep)))) { spJobStep->Name = ppszCmdNames[ lStep - 1 ]; spJobStep->StepID = ++lStepID; spJobStep->SubSystem = _T( "TSQL" ); spJobStep->DatabaseName = (LPCTSTR)_T("MFINCOMP");//pPgSelDb->m_szDbs; spJobStep->RetryAttempts = 0; spJobStep->RetryInterval = 0; spJobStep->OSRunPriority = SQLDMO::SQLDMORunPri_AboveNormal; spJobStep->Command = ppszCmdText[ lStep - 1 ];
if( ( lStepID / 3 ) < lLogCount ) { spJobStep->OnFailAction = SQLDMO::SQLDMOJobStepAction_GotoNextStep; spJobStep->OnSuccessAction = SQLDMO::SQLDMOJobStepAction_GotoNextStep; } else { spJobStep->OnFailAction = SQLDMO::SQLDMOJobStepAction_QuitWithFailure; spJobStep->OnSuccessAction = SQLDMO::SQLDMOJobStepAction_QuitWithSuccess; }
spJob->AddStepToJob( spJobStep ); } } } } catch ( IN ... ) // Handle all exceptions { throw; // Pass exception to some other handler } /////////////////////////////////
spJob->StartStepID = 1;
// FIX, use TrueName because Name can be an IP address spJob->ApplyToTargetServer( spSqlServer2->TrueName );
spJob->DoAlter(); spJobServer->Jobs->Refresh();
// End of SQLDMO_CreateJob() }
DoEvents();
spBackup2->Database = (LPCTSTR)spszDbs; spBackup2->Initialize = (VARIANT_BOOL)( TRUE ); spBackup2->Action = (SQLDMO::SQLDMO_BACKUP_TYPE)SQLDMO::SQLDMOBackup_Log; spBackup2->TruncateLog = (SQLDMO::SQLDMO_BACKUP_LOG_TYPE)SQLDMO::SQLDMOBackup_Log_TruncateOnly; spBackup2->BackupSetName = (LPCTSTR)_T("MFINCOMP Backup"); spBackup2->BackupSetDescription = (LPCTSTR)_T("My 1st backup"); spBackup2->Files = (LPCTSTR)_T("c:\\temp\\mfincomp.bak"); spBackup2->MediaName = (LPCTSTR)_T("Transaction wizard"); spBackup2->Restart = (VARIANT_BOOL)( TRUE ); spBackup2->Initialize = (VARIANT_BOOL)( FALSE ); // !!! If TRUE, the backup will be overwritten
spBackup2->SQLBackup( spSqlServer2 ); } } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
P.S. Also uncheck the "Start SQL Server if it is stopped" check box on the same page if you are not admin.
#define __ARMEN_H__
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
|